Chart sharing
ChartIQ's CIQ.Share class enables you to build functionality that lets your end users share live charts as well as static images of their charts.
As of version 9.0.0, you can enable sharing directly to Twitter and Microsoft Teams at the click of a button. Future versions of the library will add direct-share capability for more social media and messaging platforms.
Sharing live charts
ChartIQ provides a number of functions to facilitate sharing live charts, but we do not host the charts, and we do not provide a database in which to store each chart's configuration object.
You will need your own server to host shared charts and your own database to persist their configuration. You will also need to write your own functions to handle interactions with your database.
The most straightforward way to identify a shared chart is by a unique ID, which can then be added to the end of the URL that a user will employ to share a chart.
Lifecycle of a shared chart
Below you will find a sample lifecycle of this feature. The functions that we provide are in blue and link to their entries in the API documentation. Functions marked in red are those you must provide in the chart configuration.
Stage one: Generating and sharing a chart
- A user opens the sharing dialog.
- The sharing dialog calls
CIQ.Share.saveChartLayout
. Within that function, chartSharing.saveChartLayout is called. It will:- Call generateUniqueID or whatever method you use to generate a unique ID for the shared chart;
- Posts the chart layout to your database using that ID;
- Returns the chart’s unique ID back to
CIQ.Share.saveChartLayout
.
- Opening share dialog also calls chartSharing.generateURL, passing the unique ID. It will generate a valid URL, incorporating the unique ID, used to share that chart.
Stage two: Loading a shared chart
- When a user loads a shared URL, chartSharing.parseURL should be called to extract the chart’s unique ID from the URL and return it.
- Using this ID, the same code should then call
CIQ.Share.loadChartFromID
. This in turn calls:- chartSharing.getChartLayout, which fetches the chart data from the database and returns it;
CIQ.Share.loadChartFromLayout
, which uses that chart data to load the actual chart (hosted on your server).
Functions we provide
The functions we provide are all exposed, so you can modify this lifecycle if you wish. For example, if you have an alternate method of retrieving the shared chart data, you can bypass loadChartFromID
and call loadChartFromLayout
directly.
- CIQ.Share.saveChartLayout calls your function to persist the current chart layout to the cloud and returns an ID that can be used to retrieve the layout from the cloud.
- CIQ.Share.loadChartFromID loads a chart with the configuration for the given shareID.
- CIQ.Share.loadChartFromLayout loads a chart with the given configuration. This function is called in
loadChartFromID
.
Functions you provide
Here are some basic suggestions for the functions you must write. For the sake of demonstration, these examples use local storage in place of the database you provide.
You can add these to the config object of the chart as follows:
const generateUniqueID = () => {} // Whatever method you use to generate unique IDs
const config = getDefaultConfig({
// Your other configuration preferences...
chartSharing: {
// Returns an ID associated with the given chart layout data.
saveChartLayout: (data) => {
return new Promise((resolve, reject) => {
// Replace with your function to generate a unique ID
const id = generateUniqueID();
// Replace with your code to post the chart layout object to your database
localStorage.setItem(id, data);
resolve(id);
});
},
// Returns the chart layout data associated with the given id.
getChartLayout: (id) => {
return new Promise((resolve, reject) => {
// Replace with your code to fetch the chart layout object from your database
resolve(localStorage.getItem(id));
});
},
// Resolves a link to a chart that will load the given ID.
generateURL: (id) => {
return new Promise((resolve) =>
// Replace with your code to fetch the chart layout object from your database
resolve(`${window.location.href}?id=${id}`)
);
},
// Resolves an ID from the current URL if available.
parseURL: () => {
return new Promise((resolve) => {
const url = window.location.href,
chartID = url.match(/\?id=(.+)$/);
if (chartID && chartID.length > 1) {
resolve(chartID[1]);
}
});
},
}
});
For more instructions on using getDefaultConfig
, see the Chart Configuration tutorial, especially the Implementation section. For examples of its use, see the sample templates we provide in the examples/templates folder of the library.
Sharing static images of charts
Chart images can be shared from a simple button control using default image parameters or from a preferences dialog box that enables customization of the image.
Sharing a chart image consists of two steps:
- Rendering an image of the chart
- Uploading the image to a server to be shared
CIQ.Share.createImage creates the image of the chart. CIQ.Share.uploadImage uploads the image to a server. CIQ.Share.uploadImage is typically called in the function passed to the cb
(callback) parameter of CIQ.Share.createImage.
Note: This decoupled functionality gives you the ability to create a preferences dialog box, so images can be customized before they're uploaded.
The CIQ.Share.shareChart function wraps CIQ.Share.createImage and CIQ.Share.uploadImage, enabling rendering and uploading of an image in a single function call.
Here's how the cq-share-dialog
web component uses the CIQ.Share functions to implement chart sharing:
// Temporarily disable menu bindings.
CIQ.UI.bypassBindings = true;
CIQ.Share.createImage(
stx,
{
// A CSS selector list of DOM elements to be hidden while an image of the chart is created.
// DOM elements selected by "cq-comparison-add-label" and ".chartSize" are hidden by default.
hide: [
".stx_chart_controls",
".stx-btn-panel",
".stx_jump_today",
".stx-baseline-handle",
".ciq-edit",
".ciq-close",
"cq-marker-label"
]
},
function (data) {
// Re-enable menu bindings.
CIQ.UI.bypassBindings = false;
var id = CIQ.uniqueID();
var host = "https://share.chartiq.com";
var startOffset = stx.getStartDateOffset();
var metaData = {
layout: stx.exportLayout(),
drawings: stx.exportDrawings(),
xOffset: startOffset,
startDate: stx.chart.dataSegment[startOffset].Date,
endDate: stx.chart.dataSegment[stx.chart.dataSegment.length - 1].Date,
id: id,
symbol: stx.chart.symbol
};
var url = host + "/upload/" + id;
var payload = { id: id, image: data, config: metaData };
self.setState("share-upload");
CIQ.Share.uploadImage(data, url, payload, function (err, response) {
self.setState("share-copy");
if (err !== null) {
CIQ.alert("error: " + err);
} else {
if (shareDialog) shareDialog.innerHTML = host + response;
}
});
}
);
The CIQ.Share.createImage function is called from the dialog box created by the cq-share-dialog
web component:
The CREATE IMAGE button calls the share function of cq-share-dialog
; share calls CIQ.Share.createImage.
In the ChartIQ templates, the dialog box is opened from the <cq-share-button>
control (which is at the bottom left of the chart).
html2canvas
The CIQ.Share.createImage function relies on the third-party html2canvas script included with the library (see js/thirdparty/html2canvas.min.js). The script can be found at https://html2canvas.hertzen.com/.
Instead of taking a screenshot of your chart, html2canvas scans a DOM element, rendering all of the elements down the DOM tree as an image using HTML5 Canvas's toDataURL method. By using html2canvas, the chart engine is able to draw dynamically added elements to the image.
This approach does have some limitations, however. The html2canvas script does not render browser plug-ins like Java. The script does not work on iFrames. The html2canvas script does render most common CSS properties but may encounter some it does not recognize. Because html2canvas relies on an HTML canvas for rendering, the script has the same CORS limitations of any other canvas element. If you wish to host the script outside of your origin, a proxy is required. Additionally, canvas security features like not allowing a tainted canvas to draw are enforced by default.
drawImage
You can also create your own image rendering function (in place of CIQ.Share.createImage) using the HTML5 canvas's built-in drawImage method. By setting the source of an image to your chart container, a canvas can export an image of the chart. By default, the image is a PNG, but other image types can be specified.
You can then upload the image to a server or save it in local storage, for example:
-
Create a custom function that creates a canvas and image
CIQ.Share.createAlternateImage = function(stx, cb) { let image = document.createElement("img"); image.src = stx.chart.canvas.toDataURL("image/png"); let canvas = document.createElement("canvas"); let context = canvas.getContext("2d"); if (image && stx.chart.canvas.width && stx.chart.canvas.height) { context.drawImage(image, 0, 0, stx.chart.canvas.width, stx.chart.canvas.height); } return cb(null, canvas); };
-
Create a custom callback function that renders the canvas
function storeImageLocally(err, canvas) { CIQ.localStorage.setItem("manualImg", canvas.toDataURL("image/jpeg")); }
-
Call your custom function
CIQ.Share.createAlternateImage(stxx, storeImageLocally);
For more information on how to draw with the HTML canvas, visit MDN or W3Schools.
Enabling direct sharing buttons
To enable sharing directly to Twitter and Microsoft Teams (and, in future versions of the library, to other platforms), all you need to do is set the flag for each platform to true
in the quickLinks
property of the chartSharing
object. You then pass this object as an argument to getDefaultConfig. For example:
const config = getDefaultConfig({
// Your other configuration preferences,
chartSharing: {
quickLinks: {
twitter: true,
msteams: true,
},
},
})
This will add a button in the Sharing dialog that opens a new tweet dialog in a new window with the URL for either the chart image or, if you have set it up, the live chart.
For more instructions on using getDefaultConfig
, see the Chart Configuration tutorial, especially the Implementation section. For examples of its use, see the sample templates we provide in the examples/templates folder of the library.
Next steps
See the examples in the API documentation for: